#
# This workflow will build and run all unit tests using dotnet docker containers,
# each targeting a single version of the dotnet SDK.
#

name: dotnet-build-and-test

on:
  workflow_dispatch:
  pull_request:
    branches: ["main", "feature*"]
  merge_group:
    branches: ["main"]

env:
  COVERAGE_THRESHOLD: 80

concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
  cancel-in-progress: true

permissions:
  contents: read

jobs:
  paths-filter:
    runs-on: ubuntu-latest
    outputs:
      dotnetChanges: ${{ steps.filter.outputs.dotnet }}
    steps:
      - uses: actions/checkout@v4
      - uses: dorny/paths-filter@v2
        id: filter
        with:
          filters: |
            dotnet:
              - 'dotnet/**'
              - '**/dotnet/**'
              - 'samples/skills/**'
      # run only if 'dotnet' files were changed
      - name: dotnet tests
        if: steps.filter.outputs.dotnet == 'true'
        run: echo "Dotnet file"
      # run only if not 'dotnet' files were changed
      - name: not dotnet tests
        if: steps.filter.outputs.dotnet != 'true'
        run: echo "NOT dotnet file"
  dotnet-build-and-test:
    needs: paths-filter
    if: needs.paths-filter.outputs.dotnetChanges == 'true'
    strategy:
      fail-fast: false
      matrix:
        include:
          - {
              dotnet: "8.0",
              os: "ubuntu-latest",
              configuration: Release,
              integration-tests: true,
            }
          - { dotnet: "8.0", os: "windows-latest", configuration: Debug }
          - { dotnet: "8.0", os: "windows-latest", configuration: Release }

    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
      - name: Setup dotnet ${{ matrix.dotnet }}
        uses: actions/setup-dotnet@v3
        with:
          dotnet-version: ${{ matrix.dotnet }}
      - name: Build dotnet solutions
        shell: bash
        run: |
          export SOLUTIONS=$(find ./dotnet/ -type f -name "*.sln" | tr '\n' ' ')
          for solution in $SOLUTIONS; do
            dotnet build $solution -c ${{ matrix.configuration }} --warnaserror
          done

      - name: Run Unit Tests
        shell: bash
        run: |
          export UT_PROJECTS=$(find ./dotnet -type f -name "*.UnitTests.csproj" | grep -v -E "(Experimental.Orchestration.Flow.UnitTests.csproj|Experimental.Assistants.UnitTests.csproj)" | tr '\n' ' ')
          for project in $UT_PROJECTS; do
            dotnet test -c ${{ matrix.configuration }} $project --no-build -v Normal --logger trx --collect:"XPlat Code Coverage" --results-directory:"TestResults/Coverage/" -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.ExcludeByAttribute=ObsoleteAttribute,GeneratedCodeAttribute,CompilerGeneratedAttribute,ExcludeFromCodeCoverageAttribute
          done

      - name: Run Integration Tests
        shell: bash
        if: github.event_name != 'pull_request' && matrix.integration-tests
        run: |
          export INTEGRATION_TEST_PROJECTS=$(find ./dotnet -type f -name "*IntegrationTests.csproj" | grep -v "Experimental.Orchestration.Flow.IntegrationTests.csproj" | tr '\n' ' ')
          for project in $INTEGRATION_TEST_PROJECTS; do
            dotnet test -c ${{ matrix.configuration }} $project --no-build -v Normal --logger trx
          done
        env:
          AzureOpenAI__Label: azure-text-davinci-003
          AzureOpenAIEmbedding__Label: azure-text-embedding-ada-002
          AzureOpenAI__DeploymentName: ${{ vars.AZUREOPENAI__DEPLOYMENTNAME }}
          AzureOpenAIEmbeddings__DeploymentName: ${{ vars.AZUREOPENAIEMBEDDING__DEPLOYMENTNAME }}
          AzureOpenAI__Endpoint: ${{ secrets.AZUREOPENAI__ENDPOINT }}
          AzureOpenAIEmbeddings__Endpoint: ${{ secrets.AZUREOPENAI_EASTUS__ENDPOINT }}
          AzureOpenAI__ApiKey: ${{ secrets.AZUREOPENAI__APIKEY }}
          AzureOpenAIEmbeddings__ApiKey: ${{ secrets.AZUREOPENAI_EASTUS__APIKEY }}
          Planners__AzureOpenAI__ApiKey: ${{ secrets.PLANNERS__AZUREOPENAI__APIKEY }}
          Planners__AzureOpenAI__Endpoint: ${{ secrets.PLANNERS__AZUREOPENAI__ENDPOINT }}
          Planners__AzureOpenAI__DeploymentName: ${{ vars.PLANNERS__AZUREOPENAI__DEPLOYMENTNAME }}
          Planners__OpenAI__ApiKey: ${{ secrets.PLANNERS__OPENAI__APIKEY }}
          Planners__OpenAI__ModelId: ${{ vars.PLANNERS__OPENAI__MODELID }}
          OpenAITextToAudio__ApiKey: ${{ secrets.OPENAITEXTTOAUDIO__APIKEY }}
          OpenAITextToAudio__ModelId: ${{ vars.OPENAITEXTTOAUDIO__MODELID }}
          OpenAIAudioToText__ApiKey: ${{ secrets.OPENAIAUDIOTOTEXT__APIKEY }}
          OpenAIAudioToText__ModelId: ${{ vars.OPENAIAUDIOTOTEXT__MODELID }}
          AzureOpenAITextToAudio__ApiKey: ${{ secrets.AZUREOPENAITEXTTOAUDIO__APIKEY }}
          AzureOpenAITextToAudio__Endpoint: ${{ secrets.AZUREOPENAITEXTTOAUDIO__ENDPOINT }}
          AzureOpenAITextToAudio__DeploymentName: ${{ vars.AZUREOPENAITEXTTOAUDIO__DEPLOYMENTNAME }}
          AzureOpenAIAudioToText__ApiKey: ${{ secrets.AZUREOPENAIAUDIOTOTEXT__APIKEY }}
          AzureOpenAIAudioToText__Endpoint: ${{ secrets.AZUREOPENAIAUDIOTOTEXT__ENDPOINT }}
          AzureOpenAIAudioToText__DeploymentName: ${{ vars.AZUREOPENAIAUDIOTOTEXT__DEPLOYMENTNAME }}
          Bing__ApiKey: ${{ secrets.BING__APIKEY }}
          OpenAI__ApiKey: ${{ secrets.OPENAI__APIKEY }}

      # Generate test reports and check coverage
      - name: Generate test reports
        uses: danielpalme/ReportGenerator-GitHub-Action@5.2.4
        with:
          reports: "./TestResults/Coverage/**/coverage.cobertura.xml"
          targetdir: "./TestResults/Reports"
          reporttypes: "JsonSummary"
          assemblyfilters: "+Microsoft.SemanticKernel.Abstractions;+Microsoft.SemanticKernel.Core;+Microsoft.SemanticKernel.PromptTemplates.Handlebars;+Microsoft.SemanticKernel.Connectors.OpenAI;+Microsoft.SemanticKernel.Yaml;+Microsoft.SemanticKernel.Agents.Abstractions;+Microsoft.SemanticKernel.Agents.Core;+Microsoft.SemanticKernel.Agents.OpenAI"

      - name: Check coverage
        shell: pwsh
        run: .github/workflows/check-coverage.ps1 -JsonReportPath "TestResults/Reports/Summary.json" -CoverageThreshold $env:COVERAGE_THRESHOLD

  # This final job is required to satisfy the merge queue. It must only run (or succeed) if no tests failed
  dotnet-build-and-test-check:
    if: always()
    runs-on: ubuntu-latest
    needs: [dotnet-build-and-test]
    steps:
      - name: Get Date
        shell: bash
        run: |
          echo "date=$(date +'%m/%d/%Y %H:%M:%S')" >> "$GITHUB_ENV"

      - name: Run Type is Daily
        if: ${{ github.event_name == 'schedule' }}
        shell: bash
        run: |
          echo "run_type=Daily" >> "$GITHUB_ENV"

      - name: Run Type is Manual
        if: ${{ github.event_name == 'workflow_dispatch' }}
        shell: bash
        run: |
          echo "run_type=Manual" >> "$GITHUB_ENV"

      - name: Run Type is ${{ github.event_name }}
        if: ${{ github.event_name != 'schedule' && github.event_name != 'workflow_dispatch'}}
        shell: bash
        run: |
          echo "run_type=${{ github.event_name }}" >> "$GITHUB_ENV"

      - name: Fail workflow if tests failed
        id: check_tests_failed
        if: contains(join(needs.*.result, ','), 'failure')
        uses: actions/github-script@v6
        with:
          script: core.setFailed('Integration Tests Failed!')

      - name: Fail workflow if tests cancelled
        id: check_tests_cancelled
        if: contains(join(needs.*.result, ','), 'cancelled')
        uses: actions/github-script@v6
        with:
          script: core.setFailed('Integration Tests Cancelled!')
